﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Threading;
using System.IO;
using System.IO.IsolatedStorage;
using Microsoft.Phone.Reactive;
// Microsoft.Phone.Shell provides access to the ApplicationBar
using Microsoft.Phone.Shell;
// System.Device.Location provides access to the Location Service
using System.Device.Location;
using PERenaud;
using System.Collections.ObjectModel;
using Microsoft.Phone.Tasks;

namespace ReactiveLocationEmulatorSample
{
    public partial class MainPage : PhoneApplicationPage
    {
        //To pick up a number in the phone book
        PhoneNumberChooserTask phoneNumberChooserTask;


        //Collection binded to the friends list
        ObservableCollection<Friend> Friends;

        // GeocoordinateWatcher for accessing the Location Service
        GeoCoordinateWatcher watcher;

        // useEmulation determines whether live or emulated data is used
        bool useEmulation = false;

        // Threads for position and status emulation and a static bool
        Thread positionEmulationThread;
        Thread statusEmulationThread;

        // sampleInterval determines how often, in milliseconds, the location data stream is sampled
        double sampleInterval = 1000;

        // Icon buttons for the ApplicationBar
        //ApplicationBarIconButton emulationButton;
        ApplicationBarIconButton goToMapButton;
        ApplicationBarIconButton phoneBookButton;
        ApplicationBarIconButton editButton;

        public MainPage()
        {
            InitializeComponent();

            phoneNumberChooserTask = new PhoneNumberChooserTask();
            phoneNumberChooserTask.Completed += new EventHandler<PhoneNumberResult>(phoneNumberChooserTask_Completed);

            Friends = new ObservableCollection<Friend>();

            FriendsListBox.ItemsSource = Friends;

            SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

            // Initialize ApplicationBar
            ApplicationBar appBar = new ApplicationBar();
            appBar.IsVisible = true;
            this.ApplicationBar = appBar;

            //// emulationButton toggles between live and emulated data.
            //emulationButton = new ApplicationBarIconButton(new Uri("Buttons/emulation.png", UriKind.Relative));
            //emulationButton.Text = "emulate";
            //emulationButton.Click += new EventHandler(emulationButton_Click);
            //appBar.Buttons.Add(emulationButton);
            // emulationButton toggles between live and emulated data.

            phoneBookButton = new ApplicationBarIconButton(new Uri("Buttons/record.png", UriKind.Relative));
            phoneBookButton.Text = "new friend";
            phoneBookButton.Click += new EventHandler(phoneBookButton_Click);
            appBar.Buttons.Add(phoneBookButton);

            goToMapButton = new ApplicationBarIconButton(new Uri("Buttons/start.png", UriKind.Relative));
            goToMapButton.Text = "go to map";
            goToMapButton.Click += new EventHandler(goToMapButton_Click);
            appBar.Buttons.Add(goToMapButton);

            editButton = new ApplicationBarIconButton(new Uri("Buttons/pause.png", UriKind.Relative));
            editButton.Text = "edit contact";
            editButton.Click += new EventHandler(editButton_Click);
            appBar.Buttons.Add(editButton);


            // Set the informational TextBlocks to their initial value
            //emulationTextBlock.Text = useEmulation.ToString();
        }

        void editButton_Click(object sender, EventArgs e)
        {
            PopupControl.IsOpen = true;
        }

        void phoneNumberChooserTask_Completed(object sender, PhoneNumberResult e)
        {
            if (e.TaskResult == TaskResult.OK)
            {
                Friends.Add(new Friend("unknown", e.PhoneNumber, "Images/no_profile_pic.jpg"));
            }
        }

        void phoneBookButton_Click(object sender, EventArgs e)
        {
            try
            {
                phoneNumberChooserTask.Show();
            }
            catch (System.InvalidOperationException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void goToMapButton_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/MapRadar.xaml", UriKind.Relative));
        }

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            MessageBoxResult result = MessageBox.Show("Do you want to emulate your location?", "Emulation Mode", MessageBoxButton.OKCancel);
            if (result.ToString() == "OK")
            {
                useEmulation = true;
                App.emulationMode = true;
                Friends.Add(new Friend("Babar", "0645788923", "Images/Babar.jpg"));
                Friends.Add(new Friend("Patrick Sébastien", "0645789835", "Images/Patrick.jpg"));
                Friends.Add(new Friend("Charlie", "0645548927", "Images/Charlie.jpg"));
            }
            //FriendsListBox.ItemsSource = Friends;
            if (!useEmulation)
            {
                // If emulation is not used, instantiate the GeoCoordinateWatcher
                watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);

                // FromEvent is used to obtain an Observable sequence of GeoPositionChangedEventArgs
                var locationEventAsObservable = Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
                    ev => watcher.PositionChanged += ev,
                    ev => watcher.PositionChanged -= ev);

                // To make the emulation code less complicated, get a stream of GeoCoordinate objects
                // from the stream of GeoPositionChangedEventArgs provided by the watcher.PositionChanged event.
                var geoCoordinatesFromEventArgs = from ev in locationEventAsObservable
                                                  select ev.EventArgs.Position.Location;

                // Sample the observable to return only one position per sample interval. The interval can be
                // adjusted by the sampleSlider in the UI.
                var sampledLocationEvents = geoCoordinatesFromEventArgs.Sample(TimeSpan.FromMilliseconds(sampleInterval));

                // Subscribe to the filtered Observable stream. InvokePositionChanged will be called each
                // time a new GeoCoordinate arrives in the stream.
                sampledLocationEvents.Subscribe(coordinate => InvokePositionChanged(coordinate));


                // Create an observable sequence of GeoPositionStatusChanged events.
                var locationStatusEventAsObservable = Observable.FromEvent<GeoPositionStatusChangedEventArgs>(
                    ev => watcher.StatusChanged += ev,
                    ev => watcher.StatusChanged -= ev);

                // For simplicity, create a stream of GeoPositionStatus objects from the stream of
                // GeoPositionStatusChangedEventArgs objects
                var statusFromEventArgs = from ev in locationStatusEventAsObservable
                                          select ev.EventArgs.Status;

                // Subscribe to the Observable  stream. InvokeStatusChanged will be called each time
                // a new GeoPositionStatus object arrives in the stream.
                statusFromEventArgs.Subscribe(status => InvokeStatusChanged(status));

                // Start the GeoCoordinateWatcher
                watcher.Start();
            }
            else
            {
                // If emulation is being used, start a new thread and run the StartStatusEmulation method.
                // When the emulated status becomes Ready, the position emulation thread is launched
                statusEmulationThread = new Thread(StartStatusEmulation);
                statusEmulationThread.Start();

            }

            // Replace the startButton with the pauseButton and set running to true
            App.running = true;
        }
       

        /// <summary>
        /// Called from the startButton click handler when emulation is turned on. This method
        /// calls EmulateLocationEvents which returns an IEnumerable sequence of GeoCoordinates.
        /// ToObservable converts this stream to an Observable sequence. Once the Observable is
        /// created, the method is the same as the non-emulation method. The stream is sampled
        /// and then Subscribe registers a handler that is called when new location data arrives.
        /// </summary>
        void StartPositionEmulation()
        {
            var emulatedEventsToObservable = App.EmulatePositionEvents().ToObservable();

            var sampledEvents = emulatedEventsToObservable.Sample(TimeSpan.FromMilliseconds(sampleInterval));

            sampledEvents.Subscribe(coordinate => InvokePositionChanged(coordinate));

        }
        /// <summary>
        /// Called from the startButton click handler when emulation is turned on. This method
        /// calls EmulateStatusEvents which returns an IEnumerable sequence of GeoPositionStatus
        /// objects. ToObservable converts this stream to an Observable sequence. The stream is 
        /// subscribed to, registering a handler that is called when new status data arrives.
        /// </summary>
        void StartStatusEmulation()
        {
            var statusEventsToObservable = App.EmulateStatusEvents().ToObservable();

            var statusFromEventArgs = from s in statusEventsToObservable
                                      select s.Status;

            statusFromEventArgs.Subscribe(status => InvokeStatusChanged(status));
        }

        /// <summary>
        /// InvokePositionChanged is called when new location data is available (either live
        /// or emulated). It uses BeginInvoke to call another handler on the Page's UI thread.
        /// </summary>
        /// <param name="coordinate"></param>
        void InvokePositionChanged(GeoCoordinate coordinate)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() => PositionChanged(coordinate));
        }
        /// <summary>
        /// This is where the application responds to new location data. In this case, it simply
        /// displays the location information
        /// </summary>
        /// <param name="coordinate"></param>
        void PositionChanged(GeoCoordinate coordinate)
        {
            // Display the latest location information
            //latitudeTextBlock.Text = coordinate.Latitude.ToString("0.0000");
            //longitudeTextBlock.Text = coordinate.Longitude.ToString("0.0000");
        }
        /// <summary>
        /// InvokeStatusChanged is called when new location status is available (either live
        /// or emulated). It uses BeginInvoke to call another handler on the Page's UI thread.
        /// </summary>
        /// <param name="status"></param>
        void InvokeStatusChanged(GeoPositionStatus status)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() => StatusChanged(status));
        }

        /// <summary>
        /// This is where the application responds to new status information. In this case, it simply
        /// displays the location information.  If emulation is enabled and the status is Ready, a
        /// new thread is launched for position emulation. This mimics the behaviour of the live data,
        /// where location data doesn't arrive until the LocationService status is Ready.
        /// </summary>
        /// <param name="status"></param>
        void StatusChanged(GeoPositionStatus status)
        {
            // Display the current status
            //statusTextBlock.Text = status.ToString();
            if (status == GeoPositionStatus.NoData || status == GeoPositionStatus.Disabled)
                MessageBox.Show("Your GPS does not work right now. Please try again later or buy a real phone.");

            // If emulation is being used and the status is Ready, start the position emulation thread
            if (status == GeoPositionStatus.Ready && useEmulation)
            {

                positionEmulationThread = new Thread(StartPositionEmulation);
                positionEmulationThread.Start();
            }
        }

        private void FriendsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            App.currentFriend = (Friend) FriendsListBox.SelectedItem;
            PopupGrid.DataContext = App.currentFriend;
        }

        private void EditDeleteButton_Click(object sender, RoutedEventArgs e)
        {
            Friends.Remove((Friend)FriendsListBox.SelectedItem);
            PopupControl.IsOpen = false;
        }

        private void EditSaveButton_Click(object sender, RoutedEventArgs e)
        {
            PopupControl.IsOpen = false;
        }

 
    }
}
